﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EasyCLRBase;
using EasyCLRBase.AssemblyExplorer;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;

namespace EasyCLRNetwork
{
    [Export(typeof(EasyCLRBase.IClrBase))]
    [Serializable]
    [SqlUserDefinedType(
        Format.UserDefined, 
        Name = "SftpSshNet", 
        IsByteOrdered = true, 
        MaxByteSize = Constants.UserDefinedTypeMaxByte)]
    public struct UdtSshDotNetSftp : IClrBase, IBinarySerialize, INullable, IEquatable<UdtSshDotNetSftp>
    {
        private Boolean _IsNull;
        private String _Server;
        private String _User;
        private String _Password;
        private String _CurrentUpDownFile;
        private Byte _UpDownFilePercentage;
        private Int64 _CurrentUpDownFileSize; 
        private Renci.SshNet.SftpClient _Client;

        public String Server
        {
            [SqlMethod(IsDeterministic = true)]
            get { return (this._Server); }
            set { this._Server = value; }
        }
        public String User
        {
            [SqlMethod(IsDeterministic = true)]
            get { return (this._User); }
            set { this._User = value; }
        }
        public String Password
        {
            [SqlMethod(IsDeterministic = true)]
            get { return (this._Password); }
            set { this._Password = value; }
        }
        public String CurrentUpDownFile
        {
            [SqlMethod(IsDeterministic = true)]
            get { return (this._CurrentUpDownFile); }
            set { this._CurrentUpDownFile = value; }
        }
        public Byte UpDownFilePercentage
        {
            [SqlMethod(IsDeterministic = true)]
            get { return (this._UpDownFilePercentage); }
            set { this._UpDownFilePercentage = value; }
        }
        public Int64 CurrentUpDownFileSize
        {
            [SqlMethod(IsDeterministic = true)]
            get { return (this._CurrentUpDownFileSize); }
            set { this._CurrentUpDownFileSize = value; }
        }

        public bool IsNull
        {
            [SqlMethod(IsDeterministic = true)]
            get { return (this._IsNull); }
        }

        public SqlInt64 Download(SqlString remoteFile, SqlString localFile, SqlBoolean delete)
        {
            Int64 result = -1;
            this.Connect();
            using (var local = System.IO.File.Open(localFile.Value, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
            {
                var fi = this._Client.Get(remoteFile.Value);
                this._CurrentUpDownFileSize = fi.Length;
                this._CurrentUpDownFile = remoteFile.Value;
                this._Client.DownloadFile(remoteFile.Value, local, (ulong len) => { result = (Int64)len; });
                if (delete.IsNull == false && delete.IsTrue)
                    this._Client.Delete(remoteFile.Value);
            }
            return result;
        }

        public SqlInt64 Upload(SqlString remoteFile, SqlString localFile, SqlBoolean delete)
        {
            Int64 result = -1;
            this.Connect();
            using (var local = System.IO.File.Open(localFile.Value, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.None))
            {
                this._CurrentUpDownFileSize = local.Length;
                this._CurrentUpDownFile = localFile.Value;
                this._Client.UploadFile(local, remoteFile.Value, (ulong len) => { result = (Int64)len; });
            }
            if (delete.IsNull == false  && delete.IsTrue)
                System.IO.File.Delete(localFile.Value);
            return result;
        }

        public void Delete(SqlString remoteFile)
        {
            this.Connect();

            this._Client.Delete(remoteFile.Value);
        }

        private void Connect()
        {
            if (this._Client == null)
            {
                this._Client = new Renci.SshNet.SftpClient(this._Server, this._User, this._Password);
                this._Client.Connect();
            }
            else if (this._Client.IsConnected == false)
            {
                this._Client.Connect();
            }
        }

        /// <summary>
        /// Generate CLR Code for export.
        /// </summary>
        /// <returns></returns>
        public ClrExportString GetClrCreationCode()
        {
            return ClrBase.GetClrCreationCode(this.GetType());
        }
        /// <summary>
        /// Create new SSH.NET Instance with Server/User/Password
        /// </summary>
        /// <param name="str">String to decode</param>
        /// <returns></returns>
        [SqlMethod(OnNullCall = false)]
        public static UdtSshDotNetSftp Parse(SqlString str)
        {
            UdtSshDotNetSftp result = UdtSshDotNetSftp.Null;
            if (str.IsNull == false)
            {
                String[] sup = str.Value.Split(new String[] { Environment.NewLine }, 3, StringSplitOptions.RemoveEmptyEntries);
                if (sup.Length > 0)
                    result = new UdtSshDotNetSftp() { _Server = sup[0], _User = sup[1], _Password = sup[2] };
                else
                    throw new ArgumentException(String.Concat(
                        "Could not parse give String: ", str.Value, Environment.NewLine, 
                        "please ensure the following definition is defined in the string:", Environment.NewLine,
                        "<SERVER>\\r\\n<User>\\r\\n<Password>"));
            }
            return result;
        }


        public static UdtSshDotNetSftp Null
        {
            get
            {
                return new UdtSshDotNetSftp() { _IsNull = true };
            }
        }

        public void Read(System.IO.BinaryReader r)
        {
            this._IsNull = r.ReadBoolean();
            if (this._IsNull == false)
            {
                this.Server = r.ReadString();
                this.User = r.ReadString();
                this.Password = r.ReadString();
                this.CurrentUpDownFile = r.ReadString();
                this.UpDownFilePercentage = r.ReadByte();
                this.CurrentUpDownFileSize = r.ReadInt64();
            }
        }

        public void Write(System.IO.BinaryWriter w)
        {
            w.Write(this._IsNull);
            if (this.IsNull == false)
            {
                w.Write(this._Server);
                w.Write(this._User);
                w.Write(this._Password);
                w.Write(String.IsNullOrEmpty(this._CurrentUpDownFile) == true ? String.Empty : this._CurrentUpDownFile);
                w.Write(this._UpDownFilePercentage);
                w.Write(this._CurrentUpDownFileSize);
            }
        }

        public override String ToString()
        {
            return this.IsNull == true ? "NULL" : String.Concat(this.Server, Environment.NewLine, this.User, Environment.NewLine, this.Password);
        }

        public override bool Equals(object obj)
        {
            return (obj is UdtSshDotNetSftp) ? this.Equals((UdtSshDotNetSftp)obj) : false;
        }

        public override int GetHashCode()
        {
            return EasyCLRBase.HashBuilder.GetHashCode(this.Server, this.User, this.Password);
        }

        public bool Equals(UdtSshDotNetSftp other)
        {
            return String.Equals(other.Server, this.Server) && String.Equals(other.User, this.User) && String.Equals(other.Password, this.Password) ? true : false;
        }

        public String HowTo()
        {
            throw new NotImplementedException();
        }
    }
}
